Call, Response
Retrofit을 이용하다보면 자세히 파악하고 있어야 하는 몇가지 객체나 인터페이스가 있다.
Call과 Response도 그 중 하나이다.
1. Call과 Response는 비슷한 역할을 하는건가..?
Call은 요청을 하기 위한 객체이고, Response는 요청에 대한 응답이다.
Call과 Response 객체가 비슷한 역할을 하는것인지 헷갈리는 이유는 레트로핏 서비스 인터페이스에 API들을 정의할 때 리턴값을 Call객체로도 받을 수 있기 때문이였다.
Retrofit 공식 홈페이지의 API Declaration에 보면 Call<User> getUser(@Path("username") String username);
이런식으로 되어있는 예제를 확인할 수 있다. 그리고 KOTLIN SUPPORT 부분에는 suspend fun getUser(): Response<User>
이런식으로 사용이 가능하다고 되어있다.
이 부분을 조금 더 자세히 보면 아래와 같이 쓰여져있다.
Interface methods support kotlin suspend functions which directly return a
Response
object, creating and asynchronously executing the call while suspending the current function.
리턴타입으로 Response를 사용하면 서비스 인터페이스를 구체클래스로 만들때 내부적으로 Call 객체를 생성하고 실행시키는 로직을 추가해 준다는 것이다. (단, suspend 함수로 정의했을 때만.)
정리하자면, 최초에는 서비스 인터페이스의 구현체의 메서드를 호출했을 때 내부적으로 OKHttpCall을 호출하고(구현체를 생성할 때 proxy를 통해 이런 방식이 구성된다. Retrofit Github, Retrofit.java) 그 결과값을 Call 객체로 반환하였다. 코틀린에서는 코루틴을 이용해서 Call 객체가 아닌 Response 객체를 반환해줄 수 있게 된 것이다.
참고 포스트
- retrofit API Service Interface 구체화 과정이 상세하게 설명되어 있다. Retrofit-Sample Github, HwangEunmi
2. CallAdapter & Converter
CallAdapter와 Converter 모두 응답값을 변환하는 객체이다. (Converter는 요청값을 변환시키는 역할도 한다.) 이 두 객체의 차이점은
- CallAdapter: 서비스 인터페이스 메서드의 기본 리턴값은
Call<T>
이다. 이 기본 리턴값을 다른 타입으로 변환할 때 사용한다. - Converter: HTTP 요청이나 응답의 본문을 변환한다. (요청 시 객체를 JSON으로 변경하거나, 응답 시 JSON을 객체로 변환하거나.)
CallAdapter와 Converter 모두가 설정되어 있다면, 요청시에는 Converter만 거치게 되고(객체를 JSON등의 형식으로 변환하기 위해), 응답시에는 Converter를 먼저 거친 뒤 CallAdapter를 적용하게 된다.
2-1. CallAdapter
CallAdapter는 서비스 인터페이스 메서드의 리턴값을 변경할 때 사용한다. Success나 Error객체를 sealed class로 wrapping하는 등의 데이터 레이어 정책에 맞게 변경시키기 위해 사용한다.
CallAdapter를 생성하는 과정은
- CallAdapter 인터페이스를 상속받는 클래스 구현
- 내부 클래스인 Factory 객체 구현
- adapt() 메서드 구현
- Call 인터페이스를 상속받는 클래스 구현
- 메서드 오버라이드하여 구현 (enqueue()메서드가 핵심)
이렇게 만들어진 CallAdapter 객체를 적용시키기 위해서는 Retrofit 빌더 사용시 addCallAdapterFactory()메서드에 CallAdapterFactory 객체를 넣어주면 된다.
참고 포스트
- CallAdapter와 CallAdapter.Factory의 메서드에 대한 설명이 자세하게 되어있다.
Retrofit에 CallAdapter를 적용하는 법, seong-hwan Kim - Retrofit내에서의 CallAdapter의 흐름이나 CallAdapter를 통해 변환한 Sealed Class의 클래스들을 확장함수로 응용하는 예시들이 잘 나와 있다.
Network 응답 처리, 이렇게 해보는건 어떤가요? CallAdapter / Wrapper Class - CallAdapter 및 나머지 클래스 구현 설명 및 깃허브 소스코드 참고 가능한 포스트
Retrofit에서 API 성공/에러가 분리된 응답으로 반환하는 Custom Adapter 만들기, pluulove
2-2. Converter
Retrofit - API Declaration, CONVERTERS
레트로핏은 기본적으로 HTTP 응답 바디를 OKHttp의 ResponseBody로 변환하거나, 요청 시에 RequestBody 타입만을 받아들인다. 이 부분을 변경하기 위한 객체가 Converter이다.
Converter는 추가적으로 GSON, Protobuf, Scalars등이 제공되며 커스터마이징이 필요한 경우
Converter.Factory()
를 상속받는 클래스를 구현한다. 이 컨버터를 적용할 때에는 Retrofit 빌더 작성 시 addConverterFactory()메서드에 해당 컨버터를 넣어주면 된다.
커스텀 컨버터의 예시를 간단하게 작성하면 아래와 같다.
class KMAConverterFactory : Converter.Factory() {
override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
val delegate = retrofit.nextResponseBodyConverter<Any>(this, type, annotations)
return Converter<ResponseBody, Any> {
when {
Pattern.matches(".*List<.*DTO>", type.typeName) -> {
val list = createResponseList(str) { splitStr ->
//...
}
return@Converter list
}
type == WeatherDTO::class.java -> {
val list = createResponseList(str) { splitStr -> //
//...
}
return@Converter list.firstOrNull()
}
else -> {
return@Converter delegate.convert(it)
}
}
}
}
}